// created on 23.07.2007 at 20:49
using System;
using System.Collections;
using Gtk;
using Designer;
using GtkSourceView;

public class MySourceView : SourceView
{
	public MySourceView(SourceBuffer buffer,CodeEditTab tab):base(buffer)
	{
		this.tab = tab;
	}
	CodeEditTab tab;
	
	protected override bool OnKeyPressEvent(Gdk.EventKey evnt)
   	{
   		base.OnKeyPressEvent(evnt);
   		//TODO: deactivated code completion
//   	return tab.completion.KeyPress(evnt.Key,evnt.State);
		return true;
   	}
}
public class CodeEditTab : VBox, iEditTab, ICompletionWidget
{
	HBox hbox2;
	Toolbar toolbar2;
	Label label6;
	ComboBox codeLanguageCombo;
	Gtk.Action apply;
	Gtk.Action undo;
	Gtk.Action redo;
	MySourceView source;
	SourceBuffer buffer;
	Code edit;
	Code editCopy;
	public CodeCompletion completion;
	int pos;
	public CodeEditTab(Code code)
	{
		edit = code;
		editCopy = (Code)code.Clone();
		//this.code = code;
		Gtk.UIManager w1 = new Gtk.UIManager();
		Gtk.ActionGroup w2 = new Gtk.ActionGroup("Default");
		this.apply = new Gtk.Action("apply", null, null, "gtk-apply");
		this.undo = new Gtk.Action("undo", null, null, "gtk-undo");
		this.redo = new Gtk.Action("redo", null, null, "gtk-redo");
        w2.Add(this.apply, null);
        w2.Add(this.undo, null);
        w2.Add(this.redo, null);
        w1.InsertActionGroup(w2, 0);
		
		// Container child editTabs.Gtk.Notebook+NotebookChild
	   //this.vbox2 = new Gtk.VBox();
	   // Container child vbox2.Gtk.Box+BoxChild
	   this.hbox2 = new Gtk.HBox();
	   this.hbox2.Name = "hbox2";
	   // Container child hbox2.Gtk.Box+BoxChild
	   w1.AddUiFromString("<ui><toolbar name='toolbar2'><toolitem action='apply'/><toolitem action='undo'/><toolitem action='redo'/></toolbar></ui>");
	   this.toolbar2 = ((Gtk.Toolbar)(w1.GetWidget("/toolbar2")));
	   this.toolbar2.Name = "toolbar2";
	   this.toolbar2.ShowArrow = false;
	   this.toolbar2.ToolbarStyle = ((Gtk.ToolbarStyle)(0));
	   this.hbox2.Add(this.toolbar2);
	   Gtk.Box.BoxChild w56 = ((Gtk.Box.BoxChild)(this.hbox2[this.toolbar2]));
	   w56.Position = 0;
	   // Container child hbox2.Gtk.Box+BoxChild
	   this.label6 = new Gtk.Label();
	   this.label6.Name = "label6";
	   this.label6.LabelProp = "Sprache:";
	   this.hbox2.Add(this.label6);
	   Gtk.Box.BoxChild w57 = ((Gtk.Box.BoxChild)(this.hbox2[this.label6]));
	   w57.Position = 1;
	   w57.Expand = false;
	   w57.Fill = false;
	   // Container child hbox2.Gtk.Box+BoxChild
	   this.codeLanguageCombo = Gtk.ComboBox.NewText();
	   this.codeLanguageCombo.AppendText("GOOL");
	   this.codeLanguageCombo.AppendText("GML");
	   this.codeLanguageCombo.Name = "codeLanguageCombo";
	   int lang = 0;
	   switch(code.language)
	   {
	   		case "GOOL":
	   		lang = 0;
	   		break;
	   		case "GML":
	   		lang = 1;
	   		break;
	   }
	   this.codeLanguageCombo.Active = lang;
	   this.codeLanguageCombo.Changed += OnCodeLanguageChanged;
	   this.hbox2.Add(this.codeLanguageCombo);
	   Gtk.Box.BoxChild w58 = ((Gtk.Box.BoxChild)(this.hbox2[this.codeLanguageCombo]));
	   w58.Position = 2;
	   w58.Expand = false;
	   w58.Fill = false;
	   this.Add(this.hbox2);
	   Gtk.Box.BoxChild w59 = ((Gtk.Box.BoxChild)(this[this.hbox2]));
	   w59.Position = 0;
	   w59.Expand = false;
	   w59.Fill = false;
	   
	   buffer = new SourceBuffer(ResourceMgr.Instance.Win.SourceLangs[code.language]);
	   source = new MySourceView(buffer,this);
	   source.ShowLineNumbers = true;
	   buffer.Text = code.code;
	   buffer.Highlight = true;
	   Gtk.ScrolledWindow scroll = new ScrolledWindow();
	   scroll.Add(this.source);
	   this.Add(scroll);

		// Notebook tab
		CloseableTab ctab = new CloseableTab("Code: "+code.title);
        ctab.closeButton.Clicked += OnCloseActivated;
        pos = ResourceMgr.Instance.Win.editTabs.AppendPage(this, ctab);
		Gtk.Notebook.NotebookChild w61 = ((Gtk.Notebook.NotebookChild)(ResourceMgr.Instance.Win.editTabs[this]));
		w61.TabExpand = false;
		
		ResourceMgr.Instance.Win.editTabs.ShowAll();
		this.apply.Activated += OnApplyActivated;
		this.redo.Activated += OnRedoActivated;
		this.undo.Activated += OnUndoActivated;
		
		buffer.Changed += OnBufferChanged;
		
		completion = new CodeCompletion(this);
   }
   
  	void OnBufferChanged (object s, EventArgs args)
	{
		NotifyCompletionContextChanged ();
	}
	public int GetPosition()
	{
		return pos;
	}
	public void SetPosition(int pos)
	{
		this.pos = pos;
	}
	
	protected void OnApplyActivated(object o, EventArgs args)
	{
		ResourceMgr.Instance.Win.CloseEditTab(edit, true);
	}
	protected void OnCloseActivated(object o, EventArgs args)
	{
		Gtk.MessageDialog message = new MessageDialog(ResourceMgr.Instance.Win, DialogFlags.Modal, MessageType.Question, ButtonsType.None, false, "");
		message.Text = "Sollen die Änderungen übernommen werden?";
		message.Title = "Änderungen übernehmen?";
		message.AddButton(Stock.Yes, ResponseType.Yes);
		message.AddButton(Stock.No, ResponseType.No);
		message.AddButton(Stock.Cancel, ResponseType.Cancel);
		ResponseType RetVal = (ResponseType)message.Run();
		message.Destroy();
		if(RetVal == ResponseType.Yes || RetVal == ResponseType.No)
		{
			ResourceMgr.Instance.Win.CloseEditTab(edit, (RetVal == ResponseType.Yes));
		}
	}
	//TODO: buttons ausgrauen wenn kein undo/redo möglich
	protected void OnUndoActivated(object o, EventArgs args)
	{
		if(buffer.CanUndo())
			buffer.Undo();
	}
	protected void OnRedoActivated(object o, EventArgs args)
	{
		if(buffer.CanRedo())
			buffer.Redo();
	}
	protected void OnCodeLanguageChanged(object o, EventArgs args)
	{
		buffer.Language = ResourceMgr.Instance.Win.SourceLangs[codeLanguageCombo.ActiveText];
		editCopy.language = codeLanguageCombo.ActiveText;
	}

#region UnCommentCode //taken out of monodevelop sources
	public void CommentCode ()
	{
		TextMark curMark, endMark;
		TextIter start, end;
		string commentTag;
		
		if (!buffer.GetSelectionBounds (out start, out end))
			return;
		
		commentTag = "//";
		
		// Don't comment lines where no chars are actually selected (fixes bug #81632)
		if (end.LineOffset == 0)
			end.BackwardLine ();
		
		start.LineOffset = 0;
		
		endMark = buffer.CreateMark (null, end, false);
		Console.WriteLine(end.Line+"..."+buffer.LineCount);
		
		//TODO: workaround (crash if last line is selected)
		int line = (end.Line == buffer.LineCount-1) ? end.Line-1 : end.Line;
			
		while (start.Line <= line) {
			curMark = buffer.CreateMark (null, start, true);
			buffer.Insert (ref start, commentTag);
			start = buffer.GetIterAtMark (curMark);
			end = buffer.GetIterAtMark (endMark);
			start.ForwardLine ();
		}
	}
	public void UncommentCode ()
		{
			string commentTag = "//"; // as default
			TextIter textStart;
			TextIter textEnd;
			buffer.GetSelectionBounds (out textStart, out textEnd);
			if (textStart.Line == textEnd.Line)
			{ // all the code is in one line, just uncomment is text starts with comment tag
				textStart.LineOffset = 0;
				textEnd = textStart;
				textEnd.ForwardChars (commentTag.Length);
				if (textStart.GetText (textEnd) == commentTag)
					buffer.Delete (ref textStart, ref textEnd);
			}
			else
			{ // uncomment the entire lines
				int numberOfLines = textEnd.Line - textStart.Line + 1;
				TextMark mTextStart = buffer.CreateMark (null, textStart, true);
				TextMark mTextTmp = mTextStart;
				
				for (int i=0; i<numberOfLines; i++)
				{
					TextIter textTmp = buffer.GetIterAtMark (mTextTmp);
					// delete the comment tag
					textTmp.LineOffset = 0;
										
					// the user can have spaces at start of line, handling that
					TextIter textLineEnd = textTmp;
					textLineEnd.ForwardToLineEnd ();
					string trimmedText, fullText;
					int spaces;
					fullText = textTmp.GetText (textLineEnd);
					trimmedText = fullText.TrimStart ();
					spaces = fullText.Length - trimmedText.Length;
					if (trimmedText.StartsWith (commentTag))
					{
						textTmp.ForwardChars (spaces);
						textEnd = textTmp;
						textEnd.ForwardChars (commentTag.Length);
						buffer.Delete (ref textTmp, ref textEnd);
					}
					
					// setup a mark on next line
					textTmp = buffer.GetIterAtMark (mTextTmp);
					textTmp.ForwardLine ();
					mTextTmp = buffer.CreateMark (null, textTmp, true);
				}
			}
		}
#endregion
#region ICompletionWidget

		public int CursorPosition
		{
			get
			{
				return buffer.CursorPosition;
			}
		}
		event EventHandler completionContextChanged;
		void NotifyCompletionContextChanged ()
		{
			if (completionContextChanged != null)
			{
				completionContextChanged (this, EventArgs.Empty);
			}
		}

		event EventHandler ICompletionWidget.CompletionContextChanged {
			add { completionContextChanged += value; }
			remove { completionContextChanged -= value; }
		}

		CodeCompletionContext ICompletionWidget.CreateCodeCompletionContext (int triggerOffset)
		{
			TextIter iter = buffer.GetIterAtOffset (triggerOffset);
			Gdk.Rectangle rect = source.GetIterLocation (iter);
			int wx, wy;
			source.BufferToWindowCoords (Gtk.TextWindowType.Widget, rect.X, rect.Y + rect.Height, out wx, out wy);
			int tx, ty;
			source.GdkWindow.GetOrigin (out tx, out ty);

			CodeCompletionContext ctx = new CodeCompletionContext ();
			ctx.TriggerOffset = iter.Offset;
			ctx.TriggerLine = iter.Line;
			ctx.TriggerLineOffset = iter.LineOffset;
			ctx.TriggerXCoord = tx + wx;
			ctx.TriggerYCoord = ty + wy;
			ctx.TriggerTextHeight = rect.Height;
			return ctx;
		}
		
		string BufferGetSelectedText ()
		{
			if (buffer.HasSelection)
			{
				TextIter select1, select2;
				buffer.GetSelectionBounds (out select1, out select2);
				return buffer.GetText (select1, select2, true);
			}
			
			return String.Empty;
		}
		
		string ICompletionWidget.GetCompletionText (ICodeCompletionContext ctx)
		{
			return buffer.GetText (buffer.GetIterAtOffset (ctx.TriggerOffset), buffer.GetIterAtMark (buffer.InsertMark), false);
		}
		int ICompletionWidget.SelectedLength { get { return BufferGetSelectedText ().Length; } }
		void ICompletionWidget.SetCompletionText (ICodeCompletionContext ctx, string partial_word, string complete_word)
		{
			TextIter iter1, iter2;
			if (buffer.GetSelectionBounds (out iter1, out iter2)) {
				buffer.Delete (ref iter1, ref iter2);
			}
			TextIter offsetIter = buffer.GetIterAtOffset (ctx.TriggerOffset);
			TextIter endIter = buffer.GetIterAtOffset (offsetIter.Offset + partial_word.Length);
			buffer.MoveMark (buffer.InsertMark, offsetIter);
			buffer.Delete (ref offsetIter, ref endIter);
			int idx = complete_word.IndexOf ('|'); // | in the completion text now marks the caret position
			if (idx >= 0) 
				complete_word = complete_word.Remove (idx, 1);
			buffer.InsertAtCursor (complete_word);
			if (idx >= 0) {
				TextIter it = buffer.GetIterAtMark (buffer.InsertMark);
				buffer.PlaceCursor (buffer.GetIterAtOffset (it.Offset - complete_word.Length + idx));
			}
			source.ScrollMarkOnscreen (buffer.InsertMark);
		}

		int ICompletionWidget.TextLength
		{
			get
			{
				return buffer.EndIter.Offset + 1;
			}
		}

		char ICompletionWidget.GetChar (int offset)
		{
			return buffer.GetIterAtOffset (offset).Char[0];
		}

		string ICompletionWidget.GetText (int startOffset, int endOffset)
		{
			return buffer.GetText(buffer.GetIterAtOffset (startOffset), buffer.GetIterAtOffset(endOffset), true);
		}

		Gtk.Style ICompletionWidget.GtkStyle
		{
			get
			{
				return Style.Copy();
			}
		}
#endregion

	public void Close(bool save)
	{
		if(save)
		{
		edit.code = buffer.Text;
		edit.language = editCopy.language;
		}
		ResourceMgr.Instance.Win.editTabs.Remove(this);
	}
	public void Reload()
	{}
}